package jupl.file.host;

import static migool.util.HtmlParserUtil.getLoginForm;
import static migool.util.HtmlParserUtil.setHiddenInputs;
import static migool.util.HtmlParserUtil.toListNameValuePair;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jupl.file.FileUploadException;
import jupl.file.FileUploadResponse;
import jupl.file.FileUploaderBase;
import jupl.file.ftp.FTPFileUploader;
import migool.auth.Login;
import migool.auth.LoginException;
import migool.entity.CaptchaEntity;
import migool.http.client.HttpClientWrapper;
import migool.util.HtmlParserUtil;
import migool.util.LinkUtil;
import migool.util.Regex;

import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.HasAttributeRegexFilter;
import org.htmlparser.tags.FormTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.TableRow;
import org.htmlparser.tags.TableTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;

/**
 * @author Denis Migol
 * 
 */
@SuppressWarnings("unused")
public class _4FilesNetUploader extends FileUploaderBase {

	public static final String HOST = "4files.net";

	private static final String UPL_RULES_FTP = "/?act=upl_rules&ftp";
	private static final String FILES4 = "/?act=files4";
	private static final String FTP_UPLOAD = "/?act=ftp_upload";

	private static final NodeFilter FTP_SERVERS_TABLE_FILTER = new AndFilter(HtmlParserUtil.TABLE_FILTER,
			new HasAttributeFilter(HtmlParserUtil.CLASS, "t"));
	private static final NodeFilter FILES_TABLE_FILTER = new AndFilter(HtmlParserUtil.TABLE_FILTER,
			new HasAttributeFilter(HtmlParserUtil.CLASS, "t"));

	private final HttpClientWrapper client = new HttpClientWrapper();

	/**
	 * @param host
	 */
	public _4FilesNetUploader() {
		super(HOST);
	}

	private FTPSettings receiveFTPSettings() throws ParserException, IOException {
		// TODO
		final FTPSettings ret = new FTPSettings();
		ret.setUsername("2265328");
		ret.setPassword("95240558");
		ret.setFtpServers(receiveFTPServers());
		return ret;
	}

	private List<String> receiveFTPServers() throws IOException, ParserException {
		final String page = client.getToString(LinkUtil.createHttpLink(HOST, FILES4));

		final TableTag table = (TableTag) new Parser(page).parse(FTP_SERVERS_TABLE_FILTER).elementAt(0);
		final int rowCount = table.getRowCount();
		final List<String> ret = new ArrayList<String>(rowCount - 1);

		for (int i = 1; i < rowCount; i++) {
			ret.add(HtmlParserUtil.toText(table.getRow(i).getColumns()[0].getChildren()));
		}

		return ret;
	}

	private List<String> receiveLinks(final String filename) throws IOException, ParserException {
		final List<String> ret = new ArrayList<String>(2);

		String link1 = null;
		String link2 = null;

		// TODO 10000
		for (int count = 0; count < 10000 && (link1 == null || link2 == null); count++) {
			final String page = client.getToString(LinkUtil.createHttpLink(HOST, FILES4));

			final TableTag table = (TableTag) new Parser(page).parse(FILES_TABLE_FILTER).elementAt(1);
			if (table != null) {
				final TableRow[] rows = table.getRows();
				final int rowCount = table.getRowCount();
				for (int i = 0; i < rowCount; i++) {
					final TableRow row1 = rows[i];
					if (link1 == null) {
						final NodeList links = row1.getChildren().extractAllNodesThatMatch(
								new AndFilter(HtmlParserUtil.A_FILTER, new HasAttributeRegexFilter("href",
										"http\\:\\/\\/4files\\.net\\/[\\w]+\\.[\\w]+\\/")), true);
						if (links != null && links.size() > 0) {
							final LinkTag linkTag1 = (LinkTag) links.elementAt(0);
							final String text = HtmlParserUtil.toText(linkTag1.getChildren()).trim();
							if (text.equals(filename)) {
								link1 = linkTag1.extractLink();
							}
						}
					}
					if (link1 != null) {
						final NodeList links = row1.getChildren().extractAllNodesThatMatch(
								new AndFilter(HtmlParserUtil.A_FILTER, new HasAttributeFilter("href", link1)), true);
						if (links != null && links.size() > 0) {
							final TableRow row2 = rows[i + 1];
							final String href2 = ((LinkTag) row2.getChildren()
									.extractAllNodesThatMatch(HtmlParserUtil.A_FILTER, true).elementAt(0))
									.extractLink();
							final Regex regex = new Regex(href2,
									"http\\:\\/\\/free\\-share\\.ru\\/[\\d]+\\/[\\d]+\\/[\\w\\.\\_\\-]+");
							if (regex.matches()) {
								link2 = regex.getMatches()[0][0];
							}
							break;
						}
					}
				}
			}
			if (link1 == null || link2 == null) {
				try {
					Thread.sleep(30000);
				} catch (final InterruptedException e) {
				}
			}
		}

		ret.add(link1);
		ret.add(link2);
		return ret;
	}

	private void uploadFTP(final FTPSettings settings, final File file) throws IOException {
		final FTPFileUploader uploader = new FTPFileUploader();
		uploader.upload(settings.getFtpServers().get(0), settings.getUsername(), settings.getPassword(), file);
	}

	private void saveFilesFromFTP() throws IOException {
		boolean empty = true;
		// TODO 10000
		for (int tries = 0; tries < 10000 && empty; tries++) {
			final String page = client.getToString(LinkUtil.createHttpLink(HOST, FTP_UPLOAD));
			try {
				final NodeList links = new Parser(page).parse(new AndFilter(HtmlParserUtil.A_FILTER,
						new HasAttributeRegexFilter("href", "\\?act\\=ftp\\_upload\\&refr\\=[\\w]+\\&save\\=[\\d]+")));
				final int linksSize = links.size();
				empty = linksSize == 0;
				for (int i = 0; i < linksSize; i++) {
					final LinkTag link = (LinkTag) links.elementAt(i);
					final String url = LinkUtil.createHttpLink(HOST, "/" + link.extractLink());
					client.getToString(url);
					// ???
					try {
						Thread.sleep(10000);
					} catch (final Exception e) {
					}
				}
			} catch (final Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public FileUploadResponse upload(final File file) throws IOException, FileUploadException {
		FTPSettings settings;
		try {
			settings = receiveFTPSettings();
		} catch (final ParserException e) {
			throw new FileUploadException(e);
		}

		uploadFTP(settings, file);

		saveFilesFromFTP();

		final FileUploadResponse ret = new FileUploadResponse();
		try {
			ret.setUrls(receiveLinks(file.getName()));
		} catch (final ParserException e) {
			throw new FileUploadException(e);
		}
		return ret;
	}

	@Override
	public CaptchaEntity getLoginCaptcha() throws IOException {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see migool.auth.Loginable#login(migool.auth.Login)
	 */
	@Override
	public void login(final Login login) throws LoginException, IOException {
		FormTag loginForm;
		try {
			loginForm = getLoginForm(client.getToString("http://" + HOST + "/?act=login"));
		} catch (final ParserException e) {
			throw new LoginException(e);
		}
		if (loginForm == null) {
			throw new LoginException();
		}

		final Map<String, String> params = new HashMap<String, String>();
		setHiddenInputs(loginForm, params);
		params.put("log", login.getUsername());
		params.put("pass", login.getPassword());

		final HttpPost request = new HttpPost(LinkUtil.createHttpLink(HOST, loginForm.getFormLocation()));
		request.setEntity(new UrlEncodedFormEntity(toListNameValuePair(params)));
		final String response = client.requestToString(request);

		// System.out.println(response);

		if (!response.contains(login.getUsername())) {
			throw new LoginException("Not logged");
		}
	}

	private class FTPSettings {
		private int port = 21;

		private String username;

		private String password;

		private List<String> ftpServers = new ArrayList<String>();

		/**
		 * @param port
		 *            the port to set
		 */
		public void setPort(final int port) {
			this.port = port;
		}

		/**
		 * @return the port
		 */
		public int getPort() {
			return port;
		}

		/**
		 * @param username
		 *            the username to set
		 */
		public void setUsername(final String username) {
			this.username = username;
		}

		/**
		 * @return the username
		 */
		public String getUsername() {
			return username;
		}

		/**
		 * @param password
		 *            the password to set
		 */
		public void setPassword(final String password) {
			this.password = password;
		}

		/**
		 * @return the password
		 */
		public String getPassword() {
			return password;
		}

		/**
		 * @param ftpServers
		 *            the ftpServers to set
		 */
		public void setFtpServers(final List<String> ftpServers) {
			this.ftpServers = ftpServers;
		}

		/**
		 * @return the ftpServers
		 */
		public List<String> getFtpServers() {
			return ftpServers;
		}
	}

	public static void main(final String[] args) throws Exception {
		final _4FilesNetUploader uploader = new _4FilesNetUploader();
		uploader.login(new Login("login", "password"));

		final List<String> links = uploader.upload(new File("d:\\1.html")).getUrls();
		if (links != null) {
			for (final String link : links) {
				System.out.println(link);
			}
		}
	}
}
